#ifndef _BLOCKLINKEDLIST_H
#define	_BLOCKLINKEDLIST_H

/*
 * BlockLinkedList.h
 *
 *
 * Created on February 2, 2005, 4:26 PM
 *  Modified on 2/2/08
 *      CGP; abstracted out common base class, BlockLinkedList, for a linked list of blocks. The FreeList and BlockGroup
 *          classes will be extensions of this.
 *  Modified on 1/25/09; CGP; Converted to C++
 *
 */

#include "FSBlock.h"
#include "Util.h"


/**
 * This class represents a non-contiguous sequence of blocks on the disk. This can be used to represent
 * sequential files. This utilizes 32 bit pointers ('int' values) to link together disk blocks. Thus,
 * part of the disk blocks are used for representing the allocation of blocks. The pointers
 * are stored as the first four bytes of each block ("next pointers").
 * Block numbers used in this class are the same
 * as block numbers used in the Disk class.
 *
 * <p>
 * The <b>Rewind</b> method enables
 * you to start accessing a BlockLinkedList at its first block. The <b>GetCurrentBlock</b> method returns
 * the current block in the BlockLinkedList, and <b>GetNextBlock</b> advances the BlockLinkedList to its
 * next block. Thus, <b>GetCurrentBlock</b> and <b>GetNextBlock</b> together act as a sequential block
 * access operation. You can change the contents of a block on Disk using the <b>Replace</b> method.
 *
 * This class does not provide a way to write the BlockLinkedList data members to disk. E.g., it does
 * not provide a way to write the starting block number or ending block number for a BlockLinkedList
 * to disk. To create a new BlockLinkedList, the caller decides on the block that forms the
 * start of the linked list, then calls the Initialize method of this class. Other blocks
 * can be added to that list if they are not yet part of a list, using AddBlock.
 *
 * @author Christopher G. Prince
 */
class BlockLinkedList {
private:
    void DefaultInit();
	
protected:
	void Init(BlockLinkedList *b);

public:

    /** Default initialization for all data members. Does not modify the Disk.
     */
    BlockLinkedList();

    /** Uses the disk and block size given to initialize those data members
     * of the object. Other data members get default initialization.
     * Does not modify the Disk.
     *
     * @param disk to use in operations on the BlockLinkedList thus constructed.
     * @param blockSize size of the blocks in this BlockLinkedList.
     */
    BlockLinkedList(Disk *disk, unsigned int blockSize);

    /** Create a new BlockLinkedList using the disk, and block size of another BlockLinkedList.
     * Does not modify the Disk.
     *
     * @param b Data member values are copied over from this other BlockLinkedList.
     */
    BlockLinkedList(BlockLinkedList *b);

    /** @return the Disk object associated with this BlockLinkedList.
     */
    Disk *GetDisk();

    /** Generates a one block BlockLinkedList. Modifies the disk. Assumes the disk
     * and block size data members of this BlockLinkedList have been properly initialized.
     *
     * @param blockNumber is the block number on disk of the first (and only) block
     *      of this BlockLinkedList.
     *      Resets the data in the m_buffer of this block before writing to disk.
     */
    bool Initialize(int blockNumber);

    /** Returns the amount of "usable" data within each block on disk.
     *
     * @return Returns the block size in bytes of the blocks associated with the BlockLinkedList.
     * The returned value will be 4 bytes less than the block
     * size of the Disk associated with this BlockLinkedList because the first four bytes of each
     * block's m_buffer are reserved for the "next pointer".
     */
    int GetBlockSize();

    /** Return the number of blocks currently in this BlockLinkedList.
     */
    int GetNumberOfBlocks();

    /** Unlinks the block that is the starting block of this BlockLinkedList. 
      * Makes changes on disk.
     * @return the Block representing the unlinked block. 
     * This Block isn't linked into any BlockLinkedList.
     * The next pointer of the Block is now 0.
     * It is the responsibility of the caller to deallocate the returned Block.
     *
     */
    FSBlock *UnlinkBlock(
			);

    /** Add another block to the end of this BlockLinkedList. Makes changes on disk.
     * Assumes that the ending block number for this BlockLinkedList is valid.
     * Does *not* change the state of the m_seek current block, but if the
     * m_seek block is changed by this AddBlock, on disk, this will refresh
     * the m_seek block into RAM.
     *
     * @param newBlock is the new Block. This block should have already
     * been removed from the BlockLinkedList upon which it was previously.
     * Resets the data in the m_buffer of this block before writing to disk.
     *
     */
    bool AddBlock(FSBlock *newBlock
				  );

    
    /** Return the starting block number for the BlockLinkedList.
     */
    int GetStartBlockNumber();

    /** Return the ending block number for the BlockLinkedList. 
     */
    int GetEndBlockNumber();

    /** After this call, GetCurrentBlock will return the first
     * block in the BlockLinkedList.
     * After this call, a call to GetNextBlock will 
     * seek to the second Block (if any) in the BlockLinkedList.
     * No changes are made to disk.
     */
    void Rewind(
            );

    /** Returns a reference to the current Block in the Rewind/  GetNextBlock sequence.
     */
    FSBlock *GetCurrentBlock();

    /** Seeks the BlockLinkedList to the next block, unless GetCurrentBlock() returns null.
     *  Changes the value returned by GetCurrentBlock()
     *  to the next Block in the file, unless no more Blocks exist. 
     *  In this case, GetCurrentBlock()
     *  will now return null. No changes are made to disk.
     */
    void GetNextBlock(
                );

    /** Outputs to the console
     * the sequence of block numbers comprising this BlockLinkedList. Rewind state is
     * changed by this call.
    */
    void Output();

    /** The idea in this method is to replace the data of the m_seek Block (i.e., the
    	* GetCurrentBlock() Block) but to leave the link structure of the BlockLinkedList alone
    	* and unchanged.
    	* Replaces the last blk.GetBlockSize()-4 bytes of the GetCurrentBlock() Block on Disk
     * of this BlockLinkedList with the last blk.GetBlockSize()-4 bytes of the m_buffer of the block
     * given as a parameter. The first four bytes of the m_seek Block (i.e., the
     * GetCurrentBlock() Block) are retained. 
     * Modifies the Disk.
     */
    bool Replace(FSBlock *blk);

    /** Count the number of blocks in a BlockLinkedList by traversing the Blocks from the start
     * Block to the end Block. This may be useful for debugging. Changes the Rewind state
     * of the BlockLinkedList.
     */
    int CountBlocks();

    // Menu driven testing
    static void Test();

protected:
    /** Data members
     */
    Disk *m_disk;

    unsigned int m_blockSize;
    unsigned int m_numberOfBlocks; // number of blocks in the linked list

    unsigned int m_startBlockNumber; // block at the start of the linked list
    unsigned int m_endBlockNumber; // block at the end of the linked list

    FSBlock *m_seek; // current position in iterating through list
	
};

#endif // _BLOCKLINKEDLIST_H


